package com.halden.mycache.support.saveStrategy.AOFSaveStrategy.impl;

import com.halden.mycache.core.Impl.CacheValue;
import com.halden.mycache.core.Impl.MyCache;
import com.halden.mycache.support.saveStrategy.AOFSaveStrategy.IAOFSaveMode;
import com.halden.mycache.support.saveStrategy.AOFSaveStrategy.IAOFSaveStrategy;
import com.halden.mycache.support.saveStrategy.AOFSaveStrategy.SaveContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Map;

/**
 * @Author: halden
 * @Data:2023/2/15 16:33
 * @Description: aof实现类
 */
public class AOFSaveStrategy<K,V> implements IAOFSaveStrategy<K,V> {

    /**
     * 可复用的fileChannel
     */
    private FileChannel channel;

    /**
     * 存储模式
     */
    private IAOFSaveMode saveMode;

    /**
     * 指定字符集
     */
    private static final String charset = "UTF-8";

    /**
     * 日志对象
     */
    private static Logger logger = LogManager.getLogger(AOFSaveStrategy.class);

    /**
     * 能存储的记录条数上限
     */
    private long maxRecord;

    /**
     * 现在写入的位置，也是未来读取的开始位置
     */
    private long position = 0;

    /**
     * 这一次覆盖写入的记录数
     */
    private long recordCount = 0;

    @Override
    public void setSaveMode(IAOFSaveMode aofSaveMode) {
        this.saveMode = aofSaveMode;
    }

    @Override
    public void setMaxRecord(long maxRecord) {
        this.maxRecord = maxRecord;
    }

    @Override
    public void startStrategy(String fileName) {
        try {
            File file = new File(fileName);
            File parentFile = file.getParentFile();
            if (!parentFile.exists()){
                parentFile.mkdirs();
            }
            file.deleteOnExit();
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            this.channel = randomAccessFile.getChannel();
        } catch (FileNotFoundException e) {
            logger.error(e.getMessage());
        }
    }

    @Override
    public void save(SaveContext saveContext) {
        String save = saveMode.save(saveContext);
        try {
            channel.write(ByteBuffer.wrap(save.getBytes(charset)), position);
            this.recordCount++;
            position = channel.position();
            // 已经写满，进行覆盖
            if (recordCount == maxRecord){
                recordCount = 0;
                position = 0;
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }

    @Override
    public void readSave(File file, MyCache<K,V> myCache) {
        try {
            StringBuilder builder = new StringBuilder();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int byteNum = channel.read(buffer, position);
            // 第一次读完
            while(byteNum != -1){
                // 读写切换（内部游标操作）
                buffer.flip();
                // buffer中是否有内容
                while (buffer.hasRemaining()){
                    char c = (char) buffer.get();
                    builder.append(c);
                    //读到了\n，说明读完了一个context对象
                    if (c == '\n'){
                        String s = builder.toString();
                        SaveContext context = saveMode.read(s);
                        invoke(context, myCache);
                        //清空stringbuilder
                        builder.setLength(0);
                    }
                }
                // 清空缓冲区
                buffer.clear();
                byteNum = channel.read(buffer);
            }
            // 开始从0读到position
            int read = 0;
            byteNum = channel.read(buffer,0);
            out: while (read <= position){
                // 读写切换（内部游标操作）
                buffer.flip();
                // buffer中是否有内容
                while (buffer.hasRemaining()){
                    char c = (char) buffer.get();
                    read++;
                    builder.append(c);
                    //读到了\n，说明读完了一个context对象
                    if (c == '\n'){
                        String s = builder.toString();
                        SaveContext context = saveMode.read(s);
                        invoke(context, myCache);
                        //清空stringbuilder
                        builder.setLength(0);
                    }
                    // 读到了position，停止
                    if (read == position){
                        buffer.clear();
                        break out;
                    }
                }
                // 清空缓冲区
                buffer.clear();
                byteNum = channel.read(buffer);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    private void invoke(SaveContext context, MyCache<K,V> myCache) {
        Method method = context.getMethod();
        Object[] params = context.getParams();
        try {
            method.invoke(myCache, params);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    @Override
    public void close() {
        try {
            channel.close();
            logger.info("aof close successfully");
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}
